package com.lalilu.remote

import kotlinx.coroutines.*
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import kotlin.coroutines.CoroutineContext

open class UdpService(
    private val port: Int,
    var bufferSize: Int = 1024
) : CoroutineScope {
    override val coroutineContext: CoroutineContext = Dispatchers.IO
    private var tempPacket = DatagramPacket(ByteArray(bufferSize), bufferSize)
        get() {
            if (field.data.size != bufferSize) {
                field = DatagramPacket(ByteArray(bufferSize), bufferSize)
            }
            return field
        }

    private var sendSocket = DatagramSocket()
        get() {
            if (field.isClosed) field = DatagramSocket()
            return field
        }

    private var listenSocket = DatagramSocket(port)
        get() {
            if (field.isClosed) field = DatagramSocket(port)
            return field
        }

    protected fun broadcast(packet: DatagramPacket) {
        packet.address = InetAddress.getByName(BROADCAST_SEND_ADDRESS)
        packet.port = port
        send(packet = packet, broadcast = true)
    }

    protected fun send(packet: DatagramPacket, broadcast: Boolean = false) =
        launch(Dispatchers.IO) {
            sendSocket.use {
                it.broadcast = broadcast
                it.send(packet)
            }
        }

    fun startListen(callback: suspend (packet: DatagramPacket) -> Unit = {}) =
        launch(Dispatchers.IO) {
            listenSocket.use { socket ->
                while (isActive && !socket.isClosed) {
                    socket.runCatching {
                        receive(tempPacket)
                        callback(tempPacket)
                        onReceive(tempPacket)
                    }
                }
            }
        }

    fun stopListen() {
        if (!listenSocket.isClosed) {
            listenSocket.close()
        }
    }

    open suspend fun onReceive(packet: DatagramPacket) = withContext(Dispatchers.IO) {

    }

    companion object {
        const val UDP_LISTEN_ADDRESS = "0.0.0.0"
        const val BROADCAST_SEND_ADDRESS = "255.255.255.255"
    }
}